/*
 * 描述： <描述>
 * 修改人： rain
 * 修改时间： 2016年8月9日
 * 项目： rainhy-core
 */
package com.rainhy.core.util;

import java.io.Writer;
import java.util.Map;
import java.util.WeakHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.Xpp3DomDriver;
import com.thoughtworks.xstream.mapper.CannotResolveClassException;
import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.mapper.MapperWrapper;

/**
 * XStream XML 工具
 * 
 * @author rain
 * @version [版本号, 2016年8月9日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class XStreamUtils {
    
    /** 日志 */
    private static Logger logger = LoggerFactory.getLogger(XStreamUtils.class);
    
    /** XStream映射(主要用作缓存) */
    private static Map<Class<?>, XStream> xstreamMapping = new WeakHashMap<Class<?>, XStream>();
    
    /**
     * 
     * 获取 XStream 对象<br>
     * 自动跳过空属性
     *
     * @param classType 转换的类型
     * 
     * @return XStream [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年8月10日]
     * @author rain
     */
    public static XStream getXStream(Class<?> classType) {
        return getXStream(classType, true, true);
    }
    
    /**
     * 
     * 获取 XStream 对象
     *
     * @param classType 转换的类型
     * @param isSkipOverElement 是否跳过空属性
     * @param isNewLine 是否新增一行
     * 
     * @return XStream [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年8月10日]
     * @author rain
     */
    public static XStream getXStream(Class<?> classType, boolean isSkipOverElement, boolean isNewLine) {
        if (xstreamMapping.containsKey(classType)) {
            return xstreamMapping.get(classType);
        }
        
        HierarchicalStreamDriver domDriver = null;
        if (isNewLine) {
            domDriver = new Xpp3DomDriver(new NoNameCoder());
        } else {
            domDriver = new Xpp3DomDriver(new NoNameCoder()) {
                @Override
                public HierarchicalStreamWriter createWriter(Writer out) {
                    return new PrettyPrintWriter(out, getNameCoder()) {
                        // 行结束时不加多余的空格 
                        @Override
                        protected void endOfLine() {
                            return;
                        }
                        
                        // 换行不加换行符
                        @Override
                        protected String getNewLine() {
                            return "";
                        }
                    };
                }
            };
        }
        
        XStream xstream = null;
        if (isSkipOverElement) {
            xstream = new XStream(domDriver) {
                @Override
                protected MapperWrapper wrapMapper(MapperWrapper next) {
                    return createSkipOverElementMapperWrapper(next);
                }
            };
        } else {
            xstream = new XStream(domDriver);
        }
        xstream.autodetectAnnotations(true);    // 启用注解
        xstream.aliasSystemAttribute(null, "class"); // 去掉 class 属性(主要是 Object to XML 时)
        xstream.processAnnotations(classType);
        xstreamMapping.put(classType, xstream);
        
        return xstream;
    }
    
    /**
     * 
     * 创建一个过滤掉"错误转换属性"的MapperWrapper
     * 
     * @param mapper
     * 
     * @return MapperWrapper [返回类型说明]
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    private static MapperWrapper createSkipOverElementMapperWrapper(Mapper mapper) {
        return new MapperWrapper(mapper) {
            @SuppressWarnings("rawtypes")
            @Override
            public Class realClass(String elementName) {
                try {
                    return super.realClass(elementName);
                } catch (CannotResolveClassException e) {
                    logger.warn("XML转换到Object时. 对应字段 {} 不存在. ", elementName);
                    return null;
                }
            }
        };
    }
    
}
